Except where otherwise noted, this content is Copyright (c) 2020, RTE and licensed under a CC-BY-4.0 license.
Welcome to this new tutorial. Off course Hadar is well designed to compute study for network adequacy. You can launch Hadar to compute adequacy for the next second or next year.
But Hadar can also be used like a asset investment tool. In this example, thanks to Hadar, we will make the best choice for renewable energy and network investment.
We have a small region, with metropole which doesn’t produce anything, a nuclear plan and two small cities with production.
First step parse data with pandas (and plot them)
import numpy as np import pandas as pd import hadar as hd import plotly.graph_objects as go
a = pd.read_csv('a.csv', index_col='date') fig = go.Figure() fig.add_traces(go.Scatter(x=a.index, y=a['consumption'], name='load')) fig.add_traces(go.Scatter(x=a.index, y=a['gas'], name='gas')) fig.update_layout(title_text='Node A', yaxis_title='MW')
b = pd.read_csv('b.csv', index_col='date') fig = go.Figure() fig.add_traces(go.Scatter(x=b.index, y=b['consumption'], name='load')) fig.update_layout(title_text='Node B (only consumption)', yaxis_title='MW')
c = pd.read_csv('c.csv', index_col='date') fig = go.Figure() fig.add_traces(go.Scatter(x=c.index, y=c['nuclear'], name='load')) fig.update_layout(title_text='Node C (only production)', yaxis_title='MW')
d = pd.read_csv('d.csv', index_col='date') fig = go.Figure() fig.add_traces(go.Scatter(x=d.index, y=d['consumption'], name='load')) fig.add_traces(go.Scatter(x=d.index, y=d['eolien'], name='eolien')) fig.update_layout(title_text='Node D', yaxis_title='MW')
Next step, code this network with Hadar
line = np.ones(8760) * 2000 # 2000 MW
base = hd.Study(horizon=8760)\ .network()\ .node('a')\ .consumption(name='load', cost=10**6, quantity=a['consumption'])\ .production(name='gas', cost=80, quantity=a['gas'])\ .node('b')\ .consumption(name='load', cost=10**6, quantity=b['consumption'])\ .node('c')\ .production(name='nuclear', cost=50, quantity=c['nuclear'])\ .node('d')\ .consumption(name='load', cost=10**6, quantity=d['consumption'])\ .production(name='eolien', cost=20, quantity=d['eolien'])\ .link(src='a', dest='b', cost=5, quantity=line)\ .link(src='b', dest='c', cost=5, quantity=line)\ .link(src='c', dest='a', cost=5, quantity=line)\ .link(src='c', dest='b', cost=10, quantity=line)\ .link(src='c', dest='d', cost=10, quantity=line)\ .link(src='d', dest='c', cost=10, quantity=line)\ .build()
optimizer = hd.LPOptimizer()
def compute_cost(study): res = optimizer.solve(study) agg = hd.ResultAnalyzer(study=study, result=res) return agg.get_cost().sum(axis=1), res.benchmark
def print_bench(bench): print('mapper', bench.mapper) print('modeler', sum(bench.modeler)) print('solver', sum(bench.solver)) print('total', bench.total)
base_cost, bench = compute_cost(base) base_cost = base_cost[0]
An investissor want to build a solar park with solar cells. According to last last data meteo, he could except the amount of production from this park. (Solar radiation is the same on each node of network).
What is the best node to install these solar pans ? (B is excluded because there are not enough space)
park = pd.read_csv('solar.csv', index_col='date') fig = go.Figure() fig.add_traces(go.Scatter(x=park.index, y=park['solar'], name='solar')) fig.update_layout(title_text='Forecast Solar Park Power', yaxis_title='MW')
We can build one study for each different scenarios. However, Hadar can compute many scenarios at once for a more efficient compute. Result are the same. The possibility to compute many scenarios at once, is very important for next topic Stochastic Study.
def build_sparce_data(total: int, at, data) -> np.ndarray: """ Build many scenarios input where all scenario is empty but one. :param total: number of scenario to generate :param at: scenario index to fill :param data: data to fill in selected scenario :return: matrix with shape (nb_scn, horizon) where only one scenario is not zero. """ if isinstance(data, pd.DataFrame): data = data.values.flatten() sparce = np.ones((total, data.size)) sparce[at, :] = data return sparce
We use start three studies one for each node.
solar = hd.Study(horizon=8760, nb_scn=3)\ .network()\ .node('a')\ .consumption(name='load', cost=10**6, quantity=a['consumption'])\ .production(name='gas', cost=80, quantity=a['gas'])\ .production(name='solar', cost=10, quantity=build_sparce_data(total=3, at=0, data=park))\ .node('b')\ .consumption(name='load', cost=10**6, quantity=b['consumption'])\ .node('c')\ .production(name='nuclear', cost=50, quantity=c['nuclear'])\ .production(name='solar', cost=10, quantity=build_sparce_data(total=3, at=1, data=park))\ .node('d')\ .consumption(name='load', cost=10**6, quantity=d['consumption'])\ .production(name='eolien', cost=20, quantity=d['eolien'])\ .production(name='solar', cost=10, quantity=build_sparce_data(total=3, at=2, data=park))\ .link(src='a', dest='b', cost=5, quantity=line)\ .link(src='b', dest='c', cost=5, quantity=line)\ .link(src='c', dest='a', cost=5, quantity=line)\ .link(src='c', dest='b', cost=10, quantity=line)\ .link(src='c', dest='d', cost=10, quantity=line)\ .link(src='d', dest='c', cost=10, quantity=line)\ .build()
costs, bench = compute_cost(solar) costs = pd.Series(data=costs, name='cost', index=['a', 'c', 'd'])
(base_cost - costs) / base_cost * 100
a 8.070145 c 2.342062 d 2.736793 Name: cost, dtype: float64
As we can see, network is more efficient if solar park is installed one node A (8% more efficient than only 2-3% for other node)
Add an extra difficulties ! Region want to invest in a new line between A->C, D->B, A->D, D->A.
In this case, What is the best place to install solar park and what is the more usefull line to build ?
solar_line = hd.Study(horizon=8760, nb_scn=12)\ .network()\ .node('a')\ .consumption(name='load', cost=10**6, quantity=a['consumption'])\ .production(name='gas', cost=80, quantity=a['gas'])\ .production(name='solar', cost=10, quantity=build_sparce_data(total=12, at=[0, 3, 6, 9], data=park))\ .node('b')\ .consumption(name='load', cost=10**6, quantity=b['consumption'])\ .node('c')\ .production(name='nuclear', cost=50, quantity=c['nuclear'])\ .production(name='solar', cost=10, quantity=build_sparce_data(total=12, at=[1, 4, 7, 10], data=park))\ .node('d')\ .consumption(name='load', cost=10**6, quantity=d['consumption'])\ .production(name='eolien', cost=20, quantity=d['eolien'])\ .production(name='solar', cost=10, quantity=build_sparce_data(total=12, at=[2, 5, 8, 11], data=park))\ .link(src='a', dest='b', cost=5, quantity=line)\ .link(src='b', dest='c', cost=5, quantity=line)\ .link(src='c', dest='a', cost=5, quantity=line)\ .link(src='c', dest='b', cost=10, quantity=line)\ .link(src='c', dest='d', cost=10, quantity=line)\ .link(src='d', dest='c', cost=10, quantity=line)\ .link(src='a', dest='c', cost=10, quantity=build_sparce_data(total=12, at=[0, 1, 2], data=line))\ .link(src='d', dest='b', cost=10, quantity=build_sparce_data(total=12, at=[3, 4, 5], data=line))\ .link(src='a', dest='d', cost=10, quantity=build_sparce_data(total=12, at=[6, 7, 8], data=line))\ .link(src='d', dest='a', cost=10, quantity=build_sparce_data(total=12, at=[9, 10, 11], data=line))\ .build()
costs2, bench = compute_cost(solar_line) costs2 = pd.DataFrame(data=costs2.reshape(4, 3), index=['a->c', 'd->b', 'a->d', 'd->a'], columns=['a', 'c', 'd'])
(base_cost - costs2) / base_cost * 100
Very interesting, new line is a game changer. D->A and D->B seem most valuable lines. If D->B is created, it’s more efficient to install solar park on node D !